package com.platform.core.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.platform.comm.common.batch.ComBatchContext;
import com.platform.comm.common.batch.ComBatchExportContext;
import com.platform.comm.common.service.ComImportService;
import com.platform.comm.entity.CommBatchInfoEntity;
import com.platform.comm.exceptions.ValidationException;
import com.platform.comm.jwt.JwtTokenUtil;
import com.platform.comm.jwt.TokenInfoUtil;
import com.platform.comm.util.CryptUtil;
import com.platform.comm.util.DateTimeUtil;
import com.platform.comm.util.StringUtil;
import com.platform.core.entity.SysDepartConfig;
import com.platform.core.service.SysDictItemService;
import com.platform.dto.DepartQo;
import com.platform.dto.DictItemQo;
import com.platform.enums.BatchJobStatus;
import com.platform.enums.BatchJobType;
import com.platform.enums.DepartType;
import com.platform.utils.ObjectConverter;
import com.platform.vo.ImportDto;
import ma.glasnost.orika.MapperFacade;
import ma.glasnost.orika.MapperFactory;
import ma.glasnost.orika.impl.DefaultMapperFactory;
import org.apache.commons.lang3.StringUtils;
import org.springframework.batch.item.database.Order;
import org.springframework.context.annotation.Scope;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static com.platform.comm.constants.GlobalConstant.BASE_REDIS_CACHE_PREFIX;


/**
 * description: 组织块批量处理
 *
 * @author lx
 */
@Service("SysDepartImportServiceImpl")
@Scope("prototype")
public class SysDepartImportServiceImpl implements ComImportService {
    private static final String NAME = "组织管理";
    private final String redisKey = String.format(BATCH_IMPORT_REPEAT_REDIS_KEY, getClass().getSimpleName());
    public final static String DEPT_TREE_REDIS = BASE_REDIS_CACHE_PREFIX + "allDept";
    private final MapperFacade mapperFacade;
    private final SysDepartServiceImpl sysDepartService;
    private final TokenInfoUtil tokenInfoUtil;
    private final SysDictItemService sysDictItemService;
    @JsonIgnore
    private final RedisTemplate<String, Object> redisTemplate;
    private final static String DEFAULT_CONDITIONS = "delflag ='0'";

    public SysDepartImportServiceImpl(SysDepartServiceImpl sysDepartService,
                                      RedisTemplate<String, Object> redisTemplate,
                                      TokenInfoUtil tokenInfoUtil,
                                      SysDictItemService sysDictItemService) {
        this.sysDepartService = sysDepartService;
        this.redisTemplate = redisTemplate;
        this.tokenInfoUtil = tokenInfoUtil;
        this.sysDictItemService = sysDictItemService;
        MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
        mapperFactory
                .classMap(Map.class, SysDepartConfig.class)
                .mapNulls(false)
                .mapNullsInReverse(false)
                .byDefault()
                .register();
        mapperFactory.getConverterFactory().registerConverter(new ObjectConverter.YenumConverter());
        mapperFactory.getConverterFactory().registerConverter(new ObjectConverter.CommFlagConverter());
        mapperFactory.getConverterFactory().registerConverter(new ObjectConverter.LocalDateTimeConverter());
        this.mapperFacade = mapperFactory.getMapperFacade();
    }

    @Override
    public void translateImport(ComBatchContext context, ImportDto dto) {
        Map<String, Object> data = dto.getBucket();
        //备份信息
        dto.getOldData().putAll(dto.getBucket());
        //新增默认字段
        dto.getBucket().putAll(context.getDefaultFields());
        //组织类型转换
        DepartType type = DepartType.parse(Objects.toString(data.get("departKind"), null));
        data.put("departKind", Objects.isNull(type) ? null : type.name());
        // 处理组织类型，相关字段数据
        Object departKind = data.get("departKind");
        if (Objects.isNull(departKind)) {
            throw new RuntimeException("组织类型必填");
        }
        if (departKind.equals(DepartType.DOMAIN.name())) {
            // 映射区划
            //data.put("regionId", mappingRegion(data));

            // 更新bizCode
            data.put("bizCode", data.get("regionId"));
        } else if (departKind.equals(DepartType.NORMAL.name())) {
            // 映射机构类型
            List<DictItemQo> orgType = sysDictItemService.queryItemsByGroupCode("orgType");
            if (CollectionUtil.isNotEmpty(orgType)) {
                Map<String, String> mapping = orgType
                        .stream().collect(Collectors.toMap(DictItemQo::getItemName, DictItemQo::getItemCode));
                if (Objects.nonNull(data.get("orgType"))) {
                    data.put("orgType", mapping.get(data.get("orgType").toString()));
                }
            }
            // 更新bizCode
            data.put("bizCode", data.get("unifiedCode"));
        }
    }

    @Override
    public void validateImport(ComBatchContext context, ImportDto dto) throws ValidationException {
        DepartQo depart = mapperFacade.map(dto.getBucket(), DepartQo.class);
        List<String> messages = validate(depart);

        // 转换父组织，名称值转换为对于ID
        validateParent(depart, messages, dto);
        // 验证不同的组织类型，对应字段是否填写
        if (!StringUtils.isBlank(depart.getDepartKind()) && StringUtils.isNotEmpty(depart.getParentId())) {
            validateDepartKind(depart, messages);
            // 验证上级层级下建立组织限制
            validateParentKind(depart, messages);
        }

        if (!messages.isEmpty()) {
            throw new ValidationException(String.join(",", messages));
        }
    }

    private void validateParent(DepartQo depart, List<String> messages, ImportDto dto) {
        if (StringUtils.isNotEmpty(depart.getParentId())) {
            List<String> departNames = sysDepartService.queryDepartIdByName(depart.getParentId());
            if (departNames.size() == 0) {
                messages.add("根据名称[" + depart.getParentId() + "]未找到对应组织");
            } else if (departNames.size() > 1) {
                messages.add("根据名称[" + depart.getParentId() + "]未找到多个组织");
            } else {
                dto.getBucket().put("parentId", departNames.get(0));
                depart.setParentId(departNames.get(0));
            }
        }
    }

    private void validateParentKind(DepartQo depart, List<String> messages) {
        sysDepartService.queryById(depart.getParentId()).ifPresent(dept -> {
            String priorKind = dept.getDepartKind();
            if (priorKind.equals(DepartType.NORMAL.name())) {
                messages.add("上级组织为机构，其下不能新建区域或组织");
            }
        });
    }

    private void validateDepartKind(DepartQo depart, List<String> messages) {
        if (DepartType.DOMAIN.name().equals(depart.getDepartKind())) {
            //验证如果组织类型为区域，则区划代码必填
            String regionId = depart.getRegionId();
            boolean checkRegion = StringUtils.isBlank(regionId);
            if (checkRegion) {
                messages.add("区划代码不能为空");
            }
            if (!checkRegion && !Pattern.matches("^\\d{12}$", regionId)) {
                messages.add("区划代码必须为符合国家规范的12位数字");
            }
        } else if (DepartType.NORMAL.name().equals(depart.getDepartKind())) {
            // 验证如果组织类型为机构,则统一社会信用代码必填
            boolean checkUnifiedCode = StringUtils.isBlank(depart.getUnifiedCode());
            // 验证组织类型为机构时，机构类型必选
            boolean checkOrgType = StringUtils.isBlank(depart.getOrgType());
            if (checkUnifiedCode) {
                messages.add("统一社会信用代码不能为空");
            }
            if (checkOrgType) {
                messages.add("机构类型不能为空");
            }
        }
    }

    @SuppressWarnings("ConstantConditions")
    @Override
    public ImportDto repeatImport(ComBatchContext context, ImportDto dto) {
        List<String> messages = Lists.newArrayList();
        boolean unifiedCodeRepeat = false;
        String unifiedCode;
        long countUnifiedCode = 0;
        Object code = dto.getBucket().get("unifiedCode");
        String departName = dto.getBucket().get("departName").toString();
        //判断 批次内数据是否重复
        String sm3DepartName = CryptUtil.digest(departName);
        if (code != null) {
            unifiedCode = code.toString();
            String sm3UnifiedCode = CryptUtil.digest(unifiedCode);
            unifiedCodeRepeat = (redisTemplate.opsForSet().add(this.redisKey, sm3UnifiedCode) != 1);
            countUnifiedCode = sysDepartService.checkUnifiedCode(null, unifiedCode);
        }
        boolean departNameRepeat = (redisTemplate.opsForSet().add(this.redisKey, sm3DepartName) != 1);
        if (departNameRepeat) {
            messages.add("批次内组织名称存在重复");
        }
        if (unifiedCodeRepeat) {
            messages.add("批次内统一社会信用代码存在重复");
        }
        if (departNameRepeat || unifiedCodeRepeat) {
            dto.setFlag(false);
            dto.setErrMsg(messages.toString());
            return dto;
        }
        //判断数据库中是否存在重复的数据
        long countDepartName = sysDepartService.checkDepartName(null, null, departName);
        //判断数据库中是否存在重复的区划代码数据,新增区域时区划代码传入为regionId，新增机构时regionId为null
        Object regionId = dto.getBucket().get("regionId");
        long areaCode = 0;
        if (Objects.nonNull(regionId)) {
            String bizCode = dto.getBucket().get("regionId").toString();
            //查询区划代码存在条数
            areaCode = sysDepartService.checkAreaCode("", bizCode);
            if (areaCode > 0) {
                messages.add("区划代码存在重复");
            }
        }
        if (countDepartName > 0) {
            messages.add("组织名称存在重复");
        }
        if (countUnifiedCode > 0) {
            messages.add("统一社会信用代码存在重复");
        }
        if (countDepartName > 0 || countUnifiedCode > 0 || areaCode > 0) {
            dto.setFlag(false);
            dto.setErrMsg(messages.toString());
        }
        return dto;
    }

    @Override
    public CommBatchInfoEntity getNewExportJob(Long jobId) {
        JwtTokenUtil.JwtUser jwtUser = tokenInfoUtil.getUser();
        CommBatchInfoEntity dirBatchInfoEntity = new CommBatchInfoEntity();
        dirBatchInfoEntity.setId(StringUtil.getUUID());
        dirBatchInfoEntity.setDelflag("1");
        dirBatchInfoEntity.setJobId(String.valueOf(jobId));
        dirBatchInfoEntity.setJobType(this.getExportType());
        dirBatchInfoEntity.setCreateDept(jwtUser.getDepartId());
        dirBatchInfoEntity.setCreateDeptName(jwtUser.getDepartName());
        dirBatchInfoEntity.setCreateBy(jwtUser.getId());
        dirBatchInfoEntity.setCreateName(jwtUser.getUsername());
        dirBatchInfoEntity.setCreateDate(DateTimeUtil.toStdLongTimeStr(LocalDateTime.now()));
        dirBatchInfoEntity.setJobStatus(BatchJobStatus.STARTED.code);
        dirBatchInfoEntity.setJobName(this.getName());
        return dirBatchInfoEntity;
    }

    @Override
    public void insertDto(ComBatchContext context, ImportDto dto) {
        DepartQo departDto = mapperFacade.map(dto.getBucket(), DepartQo.class);
        departDto.init();
        departDto.setImportId(context.getCommDataInfoEntity().getId());
        // 插入用户及默认角色
        sysDepartService.create(departDto);
    }

    @Override
    public Map<String, String> translateExport(ComBatchContext context, ImportDto item) {
        // 翻译父组织名称至ID
        return item.getOldData().entrySet().stream()
                .collect(Collectors.toMap(Map.Entry::getKey, entry -> Objects.toString(entry.getValue(), "")));
    }

    @Override
    public String getName() {
        return NAME;
    }

    @Override
    public String getImportType() {
        return BatchJobType.DEPART_IMPORT.code;
    }

    @Override
    public String transformExport(ComBatchExportContext context, Map<String, Object> item) {
        // 组织类型转换
        DepartType type = DepartType.valueOf(Objects.toString(item.get("depart_kind"), ""));
        return type.getText();
    }


    @Override
    public String getDefaultConditions(Map<String, String> params) {
        return DEFAULT_CONDITIONS;
    }

    @Override
    public String getCustomConditions(Map<String, String> params) {
        String parentId = params.get("parentId");
        StringBuilder sb = new StringBuilder();
        sb.append("delflag ='0' ");
        if (StringUtils.isNotEmpty(parentId)) {
            sb.append("and depart_parent = ").append("'").append(parentId).append("'");
        } else {
            return null;
        }
        return sb.toString();
    }

    @Override
    public Map<String, Order> getSortedKeyMap() {
        Map<String, Order> orderMap = Maps.newHashMap();
        orderMap.put("depart_sort", Order.ASCENDING);
        return orderMap;
    }

    @Override
    public Map<String, Object> getEnumFieldMap() {
        Map<String, Object> enumFieldMap = Maps.newHashMap();
        enumFieldMap.put("depart_kind", "departType");
        return enumFieldMap;
    }

    @Override
    public String getExportType() {
        return BatchJobType.DEPART_EXPORT.code;
    }

    @SuppressWarnings("ConstantConditions")
    @Override
    public void clearRedis() {
        if (redisTemplate.hasKey(this.redisKey)) {
            redisTemplate.delete(this.redisKey);
        }
        if (redisTemplate.hasKey(DEPT_TREE_REDIS)) {
            redisTemplate.delete(DEPT_TREE_REDIS);
        }

    }
}
